import axios, {AxiosRequestConfig, AxiosResponse} from 'axios'
import {PlainObject} from "./types";
import _ from 'lodash'
import Qs from 'qs'
import {convertCH} from "./utils/helper";
import {serverJsUrlTransform} from "./extend";
// import {Pagination} from "./datasource";

export type ApiMethod = 'get' | 'post' | 'put' | 'patch' | 'delete' | 'download' | 'post-json' | 'post-file' | 'invoke'

export type ApiDataType = 'json' | 'form-data' | 'form'

export interface ApiObject {
    url: string
    param?: PlainObject
    method: ApiMethod
    data?: object
    headers?: PlainObject
    config?: {
        withCredentials?: boolean
        cancelExecutor?: (cancel: Function) => void
    }
    dataType?: ApiDataType

    /**
     * 下载文件名
     */
    fileName?: string,

    /**
     * 上传文件（如果需要的话）
     */
    file?: any,
}


export type ApiFunction = (api: ApiObject) => Promise<ApiResult>;

/**
 * 组件中 Api 的声明
 */
export type Api = ApiFunction | ApiObject;

export interface ApiResult {
    rawData: object
    status: number
    headers: object
    data?: any
    pagination?: any
    success: boolean
    msg: string
    errors?: {
        [propName: string]: string
    }
}

export interface CreateAjaxOption {
    baseUrl: string
    timeout: number
}


/**
 * 创建一个 Ajax 客户端
 */
export function createAjax(createOption: CreateAjaxOption): ApiFunction {

    if (createOption.baseUrl) {
        axios.defaults.baseURL = createOption.baseUrl
        axios.defaults.timeout = createOption.timeout
        axios.defaults.timeoutErrorMessage = '网络超时'
    }

    return function (option: ApiObject) {

        //@ts-ignore
        option.method = (option.method || 'get').toLocaleLowerCase();

        //@ts-ignore
        const ax: AxiosRequestConfig = {
            ...option
        };

        switch (option.method) {
            case 'get':
                ax.method = 'GET';
                ax.params = option.data
                ax.headers = {
                    ...option.headers
                }
                break

            case 'post':
                ax.method = 'POST';
                ax.headers = {
                    'Content-Type': 'application/x-www-form-urlencoded',
                    ...option.headers
                };
                ax.data = Qs.stringify(option.data)
                break

            case 'put' :
            case 'patch':
            case 'delete':
                ax.method = option.method;
                ax.headers = option.headers;
                ax.data = option.data
                break

            case 'download':
                downLoad(createOption.baseUrl + option.url, option.fileName || 'file',
                    option.data, option.headers);
                return new Promise<ApiResult>((resolver, reject) => {
                });
                break

            case "invoke":
                ax.url = serverJsUrlTransform(option.url)
                ax.method = 'POST';
                ax.headers = {
                    'Content-Type': 'application/json',
                    ...option.headers
                };
                ax.data = JSON.stringify({args: option.data})
                break

            case 'post-json':
                ax.method = 'POST';
                ax.headers = {
                    'Content-Type': 'application/json',
                    ...option.headers
                };
                if (window['_autoConvertCH_']) {
                    const tempStr = JSON.stringify(option.data)
                    ax.data = convertCH(tempStr)
                }
                else {
                    ax.data = JSON.stringify(option.data)
                }
                break

            case 'post-file':
                //TODO 刘壮. 上传文件
                var forms = new FormData();
                ax.headers = {
                    'Content-Type': 'multipart/form-data',
                    ...option.headers
                };
                _.forOwn(option.data, (value, key) => {
                    if (key === 'files') {
                        let i = 0;
                        _.each(value, f => {
                            // @ts-ignore
                            forms.append('file' + (++i), f)
                        })
                    } else {
                        forms.append(key, value)
                    }
                });
                ax.data = forms;
                ax.method = 'POST'
                break

            default:
                throw new Error('not implements')
        }

        return new Promise<ApiResult>((resolver, reject) => {
            axios(ax).then((resolverRaw: AxiosResponse<any>) => {
                const apiResult: ApiResult = {
                    rawData: resolverRaw.data,
                    status: resolverRaw.status,
                    success: (resolverRaw.data && resolverRaw.data.success),
                    data: resolverRaw.data.data,
                    pagination: resolverRaw.data.pagination,
                    msg: (resolverRaw.data.msg),
                    errors: resolverRaw.data.errors,
                    headers: resolverRaw.headers
                }
                resolver(apiResult)

            }).catch((reason: any) => {
                reject(reason)
            })
        })
    }
}


export function downLoad(downLoadUrl: string, filename: string, data: any, header: any, isJson: boolean = false) {
    const YvanUI: any = _.get(window, 'YvanUI');
    YvanUI.loading();
    const createObjectURL = (object: any) => {
        return (window.URL) ? window.URL.createObjectURL(object) : _.get(window, 'webkitURL').createObjectURL(object)
    };

    // const formData = new FormData();
    // _.forOwn(data, (v, k) => {
    //     formData.append(k, v);
    // });
    let formData = '';

    const xhr = new XMLHttpRequest();
    xhr.open('POST', downLoadUrl);
    xhr.responseType = 'blob';
    // xhr.setRequestHeader('Authorization', $.cookie('auth'))
    if (isJson) {
        formData = data ? JSON.stringify(data) : '';
        xhr.setRequestHeader('Content-Type', 'application/json');
    } else {
        formData = data ? Qs.stringify(data) : '';
        xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
    }
    //
    if (header) {
        _.forOwn(header, (v, k) => {
            xhr.setRequestHeader(k, v);
        })
    }
    xhr.onload = function (e: any) {
        if (this.status === 200) {
            const blob = this.response;
            if (_.hasIn(window, 'navigator.msSaveOrOpenBlob')) {
                navigator.msSaveBlob(blob, filename)
                YvanUI.clearLoading();

            } else {
                const a = document.createElement('a')
                const url = createObjectURL(blob)
                a.href = url
                a.download = filename
                document.append(a)
                a.click()
                a.remove()
                window.URL.revokeObjectURL(url)
                YvanUI.clearLoading()
            }
        }
    };
    xhr.send(formData);
}




